import {
  angleUnits,
  lengthUnits,
  parameterObjectExprToLabel,
  ParameterObjectType,
  UnitInfo,
  useUnit,
} from '@amcax/base';
import { InputNumberProps } from 'antd';
import { floor, round } from 'lodash';
import { useEffect, useState } from 'react';
import { docMgr, unitManager } from '../../../module';
import { getUnitForExpression } from './utils';

const VIEW_PRECISION = 3;
const INPUT_PRECISION = 6;

export const useFormatInput = ({
  inputType,
  outerValue,
  onChange,
  inputProps,
}: {
  inputType: ParameterObjectType;
  outerValue: number | string;
  onChange?: (val: number | string) => void;
  inputProps?: InputNumberProps;
}) => {
  const hasUnit = [
    ParameterObjectType.Length,
    ParameterObjectType.Angle,
  ].includes(inputType);
  const valueIsExpression = typeof outerValue === 'string';

  const getUnitParams = (
    inputType === ParameterObjectType.Length
      ? unitManager.getLengthUnitParams
      : unitManager.getAngleUnitParams
  ).bind(unitManager) as (unit?: string) => UnitInfo<string>;

  const { lengthUnit, angleUnit } = useUnit();

  const currentUnit = getUnitParams(
    inputType === ParameterObjectType.Length ? lengthUnit : angleUnit,
  );

  const unitList =
    inputType === ParameterObjectType.Length ? lengthUnits : angleUnits;

  const [isFocus, setIsFocus] = useState(false);

  const formatter = (
    val: number | string,
    includesUnit = true,
    precision?: number,
  ) => {
    if (typeof val === 'string') {
      return [
        parameterObjectExprToLabel(val, docMgr),
        getUnitForExpression(inputType)?.name,
      ]
        .filter(Boolean)
        .join(' ');
    }

    const getNumWithPrecision = (num: number, prec: number | undefined) => {
      if (prec == null) {
        return num;
      }
      return floor(num, prec) === 0 ? 0 : round(num, prec);
    };

    const valWithPrecision = getNumWithPrecision(val, precision);

    if (!hasUnit) {
      return valWithPrecision.toString();
    }

    if (!includesUnit) {
      return `${valWithPrecision / currentUnit.multiple}`;
    }

    return `${valWithPrecision / currentUnit.multiple} ${currentUnit.name}`;
  };

  const formatterWithPrecision = (val: number | string, includesUnit = true) =>
    formatter(val, includesUnit, isFocus ? INPUT_PRECISION : VIEW_PRECISION);

  useEffect(() => {
    setValue('');
  }, [inputType]);

  useEffect(() => {
    setValue(formatterWithPrecision(outerValue));
  }, [outerValue, currentUnit, isFocus]);

  // 输入框中实际显示的字符串
  const [value, setValue] = useState(formatterWithPrecision(outerValue));

  const getValueWithLimit = (val: number) => {
    let result = val;
    if (typeof inputProps?.min === 'number') {
      result = Math.max(result, inputProps?.min);
    }

    if (typeof inputProps?.max === 'number') {
      result = Math.min(result, inputProps?.max);
    }

    return result;
  };

  const parser = (input: string | number) => {
    if (inputType === ParameterObjectType.Integer) {
      return isNaN(Number(input))
        ? outerValue
        : getValueWithLimit(Math.round(Number(input)));
    }

    if (inputType === ParameterObjectType.Real) {
      return isNaN(Number(input))
        ? outerValue
        : getValueWithLimit(Number(input));
    }
    if (typeof input === 'number') {
      return input;
    }

    const regExpString = `^\\s*(-?\\d*\\.?\\d*)\\s?(${unitList.map((u) => u.name).join('|')})?\\s*$`;
    const reg = new RegExp(regExpString);
    const regResult = reg.exec(input);

    if (regResult == null) {
      return outerValue;
    }

    const num = Number(regResult[1]);
    const unit = getUnitParams(regResult[2]);

    let numberResult = num * unit.multiple;

    if (isNaN(numberResult)) {
      return outerValue;
    }

    numberResult = getValueWithLimit(numberResult);

    return numberResult;
  };

  const onStep = (
    _,
    info: {
      type: 'up' | 'down';
    },
  ) => {
    const parsedCurrentVal = parser(value);
    if (typeof parsedCurrentVal === 'string') {
      return;
    }

    const result =
      info.type === 'up'
        ? parsedCurrentVal + currentUnit.multiple
        : parsedCurrentVal - currentUnit.multiple;

    onChange?.(result);
    setValue(formatterWithPrecision(result));
  };

  const onInput = (val: string) => {
    setValue(val);
  };

  return {
    parser,
    formatter,
    onChangeOuter: () => {
      // 由于输入状态下也有精度限制，所以可能出现无改动编辑也会影响源数据的情况
      // 对于编辑后的字符串和源数据字符串对应的情况（也就是输入框字符串不变），不触发onChange
      if (formatter(outerValue, true, INPUT_PRECISION) === value) {
        return;
      }

      const result = parser(value);
      onChange?.(result);
      setValue(formatterWithPrecision(result));
    },
    value,
    currentUnit,
    onStep,
    onInput,
    step: 1 / currentUnit.multiple,
    valueIsExpression,
    onFocus() {
      setIsFocus(true);
    },
    onBlur() {
      setIsFocus(false);
    },
  };
};
